/////////////////////////////////////////////////////////////////////////////
// This file is part of the "Java-DAP" project, a Java implementation
// of the OPeNDAP Data Access Protocol.
//
// Copyright (c) 2007 OPeNDAP, Inc.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
/////////////////////////////////////////////////////////////////////////////
using System.IO;
using NDODS.Util;

namespace NDODS.DAP
{
    /// <summary> The HeaderInputStream filters the input to only Read lines of text until
    /// the "Data:" line.  This is required because overzealous buffering in the
    /// DDSParser will Read the data as well as the DDS otherwise.
    /// 
    /// </summary>
    /// <author>  jehamby
    /// </author>
    /// <version>  $Revision: 15901 $
    /// </version>
    /// <seealso cref="DConnect">
    /// </seealso>
    public class HeaderInputStream : FilterInputStream
    {
        /// <summary> Each line is buffered here.</summary>
        private sbyte[] lineBuf;

        /// <summary> Number of bytes remaining in buffer.</summary>
        private int bytesRemaining;

        /// <summary> Current buffer offset.</summary>
        private int currentOffset;

        /// <summary> End sequence to look for: "\nData:\n"</summary>
        private sbyte[] endSequence =
            new sbyte[] {(sbyte) '\n', (sbyte) 'D', (sbyte) 'a', (sbyte) 't', (sbyte) 'a', (sbyte) ':', (sbyte) '\n'};

        /// <summary> Flag when end sequence has been found</summary>
        private bool endFound;

        /// <summary> Construct a new HeaderInputStream.</summary>
        public HeaderInputStream(Stream in_Renamed) : base(in_Renamed) {
            lineBuf = new sbyte[4096];
            bytesRemaining = currentOffset = 0;
            endFound = false;

            //System.out.Print("EndSequence: ");
            //for(int i=0; i<endSequence.length ;i++)
            //    System.out.Print(endSequence[i]+"  ");
            //System.out.Print("\n");
        }

        /// <summary> Return the number of bytes in the buffer.</summary>
        //UPGRADE_NOTE: The equivalent of method 'java.io.FilterInputStream.available' is not an override method. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1143'"
        public int available() {
            return bytesRemaining;
        }

        /// <summary> Returns that we don't support the mark() and reset() methods.</summary>
        //UPGRADE_NOTE: The equivalent of method 'java.io.FilterInputStream.markSupported' is not an override method. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1143'"
        public bool markSupported() {
            return false;
        }

        /// <summary> Reads a single byte of data</summary>
        public override int Read() {
            // if the buffer is empty, get more bytes
            if (bytesRemaining == 0 && !endFound)
                getMoreBytes();
            // if the buffer is still empty, return EOF
            if (bytesRemaining == 0)
                return - 1;
            else {
                bytesRemaining--;
                return lineBuf[currentOffset++];
            }
        }


        /// <summary> Get more bytes into buffer.  Stop when endSequence is found.</summary>
        private void getMoreBytes() {
            currentOffset = 0; // reset current array offset to 0
            int bytesRead = 0; // bytes Read so far
            int lookingFor = 0; // character in endSequence to look for
            for (; bytesRead < lineBuf.Length; bytesRead++) {
                int c = (BaseStream).ReadByte();
                if (c == - 1)
                    break; // break on EOL and return what we have so far

                lineBuf[bytesRead] = (sbyte) c;
                if (lineBuf[bytesRead] == endSequence[lookingFor]) {
                    lookingFor++;
                    if (lookingFor == endSequence.Length) {
                        endFound = true;
                        break;
                    }
                }
                else if (lineBuf[bytesRead] == endSequence[0]) {
                    // CHANGED JC
                    lookingFor = 1;
                }
                else {
                    lookingFor = 0;
                }
            }
            bytesRemaining = bytesRead; // number of bytes we've Read
        }



        /// <summary> Reads up to len bytes of data from this input stream into an array of
        /// bytes. This method blocks until some input is available.
        /// </summary>
        //UPGRADE_NOTE: The equivalent of method 'java.io.FilterInputStream.Read' is not an override method. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1143'"
        public override int Read(char[] b, int off, int len)
        {
            if (len <= 0)
            {
                return 0;
            }

            int c = Read();
            if (c == -1)
                return -1;
            b[off] = (char)c;

            // We've Read one byte successfully, let's try for more
            int i = 1;
            try
            {
                for (; i < len; i++)
                {
                    c = Read();
                    if (c == -1)
                    {
                        break;
                    }
                    b[off + i] = (char)c;
                }
            }
            catch (IOException)
            {
            }
            return i;
        }

        /// <summary> Reads up to len bytes of data from this input stream into an array of
        /// bytes. This method blocks until some input is available.
        /// </summary>
        //UPGRADE_NOTE: The equivalent of method 'java.io.FilterInputStream.Read' is not an override method. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1143'"
        public override int Read(byte[] b, int off, int len) {
            if (len <= 0) {
                return 0;
            }

            int c = Read();
            if (c == - 1)
                return - 1;
            b[off] = (byte) c;

            // We've Read one byte successfully, let's try for more
            int i = 1;
            try {
                for (; i < len; i++) {
                    c = Read();
                    if (c == - 1) {
                        break;
                    }
                    b[off + i] = (byte) c;
                }
            }
            catch (IOException) {}
            return i;
        }

        /// <summary> Skips over and discards n bytes of data from the input stream.</summary>
        //UPGRADE_NOTE: The equivalent of method 'java.io.FilterInputStream.skip' is not an override method. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1143'"
        public long skip(long n) {
            if (bytesRemaining >= n) {
                bytesRemaining = (int) (bytesRemaining - n);
                return n;
            }
            else {
                int oldBytesRemaining = bytesRemaining;
                bytesRemaining = 0;
                return oldBytesRemaining;
            }
        }
    }
}